/**
 * X3DOM JavaScript Library
 * http://www.x3dom.org
 *
 * (C)2009 Fraunhofer IGD, Darmstadt, Germany
 * Dual licensed under the MIT and GPL
 *
 * Based on code originally provided by
 * Philip Taylor: http://philip.html5.org
 */

/**
 * The Namespace container for x3dom objects.
 * @namespace x3dom
 */
var x3dom = {
    canvases : [],
    x3dNS    : "http://www.web3d.org/specifications/x3d-namespace",
    x3dextNS : "http://philip.html5.org/x3d/ext",
    xsltNS   : "http://www.w3.org/1999/XSL/x3dom.Transform",
    xhtmlNS  : "http://www.w3.org/1999/xhtml"
};

x3dom.about = {
    version  : "__X3DOM_VERSION__",
    build    : "__X3DOM_BUILD__",
    revision : "__X3DOM_REVISION__",
    date     : "__X3DOM_DATE__"
};

/**
 * The x3dom.nodeTypes namespace.
 * @namespace x3dom.nodeTypes
 */
x3dom.nodeTypes = {};

/**
 * The x3dom.nodeTypesLC namespace. Stores nodetypes in lowercase
 * @namespace x3dom.nodeTypesLC
 */
x3dom.nodeTypesLC = {};

/**
 * The x3dom.components namespace.
 * @namespace x3dom.components
 */
x3dom.components = {};

/**
 * Cache for primitive nodes (Box, Sphere, etc.)
 */
x3dom.geoCache = [];

/**
 * Global indices for the vertex/index buffers
 */
x3dom.BUFFER_IDX =
{
    INDEX      : 0,
    POSITION   : 1,
    NORMAL     : 2,
    TEXCOORD   : 3,
    TEXCOORD_0 : 3,
    COLOR      : 4,
    COLOR_0    : 4,
    ID         : 5,
    TANGENT    : 6,
    BITANGENT  : 7,
    TEXCOORD_1 : 8
};

x3dom.BRDF_LUT = "";

/**
 * Stores information about Browser and hardware capabilities
 */
x3dom.caps = { PLATFORM: navigator.platform, AGENT: navigator.userAgent, RENDERMODE: "HARDWARE" };

/**
 * Registers the node defined by @p nodeDef.
 * The node is registered with the given @p nodeTypeName and @p componentName.
 *
 * @param nodeTypeName the name of the node type (e.g. Material, Shape, ...)
 * @param componentName the name of the component the node type belongs to
 * @param nodeDef the definition of the node type
 */
x3dom.registerNodeType = function ( nodeTypeName, componentName, nodeDef )
{
    // console.log("Registering nodetype [" + nodeTypeName + "] in component [" + componentName + "]");
    if ( x3dom.components[ componentName ] === undefined )
    {
        x3dom.components[ componentName ] = {};
    }
    nodeDef._typeName = nodeTypeName;
    nodeDef._compName = componentName;
    x3dom.components[ componentName ][ nodeTypeName ] = nodeDef;
    x3dom.nodeTypes[ nodeTypeName ] = nodeDef;
    x3dom.nodeTypesLC[ nodeTypeName.toLowerCase() ] = nodeDef;
};

/**
 * Test if node is registered X3D element
 *
 * @param node
 */
x3dom.isX3DElement = function ( node )
{
    // x3dom.debug.logInfo("node=" + node + "node.nodeType=" + node.nodeType + ", node.localName=" + node.localName + ", ");
    var name = ( node.nodeType === Node.ELEMENT_NODE && node.localName ) ? node.localName.toLowerCase() : null;
    return ( name && ( x3dom.nodeTypes[ node.localName ] || x3dom.nodeTypesLC[ name ] ||
        name == "x3d" || name == "websg" || name == "route" || name == "import" || name == "export" ) );
};

/**
 * Function: x3dom.extend
 *
 * Returns a prototype object suitable for extending the given class
 * _f_. Rather than constructing a new instance of _f_ to serve as
 * the prototype (which unnecessarily runs the constructor on the created
 * prototype object, potentially polluting it), an anonymous function is
 * generated internally that shares the same prototype:
 *
 * @param f - Method f a constructor
 * @returns A suitable prototype object
 *
 * @see Douglas Crockford's essay on <prototypical inheritance at http://javascript.crockford.com/prototypal.html>.
 * @todo unify with defineClass, which does basically the same
 */
x3dom.extend = function ( f )
{
    function G () {}

    G.prototype = f.prototype || f;
    return new G();
};

/**
 * Function x3dom.getStyle
 *
 * Computes the value of the specified CSS property <tt>p</tt> on the
 * specified element <tt>e</tt>.
 *
 * @param oElm       - The element on which to compute the CSS property
 * @param strCssRule - The name of the CSS property
 *
 * @eturn - The computed value of the CSS property
 */
x3dom.getStyle = function ( oElm, strCssRule )
{
    var strValue = "";
    var style = document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle( oElm, null ) : null;
    if ( style )
    {
        strValue = style.getPropertyValue( strCssRule );
    }
    else if ( oElm.currentStyle )
    {
        strCssRule = strCssRule.replace( /\-(\w)/g, function ( strMatch, p1 ) { return p1.toUpperCase(); } );
        strValue = oElm.currentStyle[ strCssRule ];
    }

    return strValue;
};

/**
 * Utility function for defining a new class.
 *
 * @param parent the parent class of the new class
 * @param ctor the constructor of the new class
 * @param methods an object literal containing the methods of the new class
 *
 * @returns the constructor function of the new class
 */
function defineClass ( parent, ctor, methods )
{
    if ( parent )
    {
        function Inheritance () {}

        Inheritance.prototype = parent.prototype;

        ctor.prototype = new Inheritance();
        ctor.prototype.constructor = ctor;
        ctor.superClass = parent;
    }

    if ( methods )
    {
        for ( var m in methods )
        {
            ctor.prototype[ m ] = methods[ m ];
        }
    }

    return ctor;
}

/**
 * Utility function for testing a node type.
 *
 * @param object the object to test
 * @param clazz the type of the class
 * @returns true or false
 */
x3dom.isa = function ( object, clazz )
{
    /*
    if (!object || !object.constructor || object.constructor.superClass === undefined) {
        return false;
    }
    if (object.constructor === clazz) {
        return true;
    }

    function f(c) {
        if (c === clazz) {
            return true;
        }
        if (c.prototype && c.prototype.constructor && c.prototype.constructor.superClass) {
            return f(c.prototype.constructor.superClass);
        }
        return false;
    }

    return f(object.constructor.superClass);
    */

    return ( object instanceof clazz );
};

/**
 * Get Global Helper
 *
 * @returns {*}
 */
x3dom.getGlobal = function ()
{
    if ( typeof self !== "undefined" )
    {
        return self;
    }

    if ( typeof window !== "undefined" )
    {
        return window;
    }

    if ( typeof global !== "undefined" )
    {
        return global;
    }

    throw new Error( "unable to locate global object" );
};

/**
 * Array to Object Helper
 */
function array_to_object ( a )
{
    var o = {};
    for ( var i = 0; i < a.length; i++ )
    {
        o[ a[ i ] ] = "";
    }
    return o;
}

/**
 * Provides requestAnimationFrame in a cross browser way.
 *
 * @see https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/demos/common/webgl-utils.js
 */
window.requestAnimFrame = ( function ()
{
    return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function ( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element )
        {
            window.setTimeout( callback, 16 );
        };
} )();

/**
 * Toggle full-screen mode
 */
x3dom.toggleFullScreen = function ()
{
    if ( document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen )
    {
        if ( document.cancelFullScreen )
        {
            document.cancelFullScreen();
        }
        else if ( document.mozCancelFullScreen )
        {
            document.mozCancelFullScreen();
        }
        else if ( document.webkitCancelFullScreen )
        {
            document.webkitCancelFullScreen();
        }
    }
    else
    {
        var docElem = document.documentElement;
        if ( docElem.requestFullScreen )
        {
            docElem.requestFullScreen();
        }
        else if ( docElem.mozRequestFullScreen )
        {
            docElem.mozRequestFullScreen();
        }
        else if ( docElem.webkitRequestFullScreen )
        {
            docElem.webkitRequestFullScreen();
        }
    }
};
